package rtmp;



import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import util.Common;

//  +--------------+     +-------------+----------------+-------------------+
//          | Chunk Header |  =  | Basic header| Message Header |Extended Timestamp |
//          +--------------+     +-------------+----------------+-------------------+
public class RtmpPacket2 {
    static Logger logger = Logger.getLogger(RtmpPacket2.class);

    private final static String TAG = "RtmpPacket";

    protected int csid;

    private Map<Integer, HeadInfo> csidHeadInfo = new HashMap<Integer, HeadInfo>();


    protected int head_len; //头部长度


    protected int timestamp = 0;


    private boolean isDebug = false;



    protected int messageLength; //消息数据长度


    public RtmpMessage getMessageType() {
        return messageType;
    }

    protected RtmpMessage messageType; //消息类型

    private ByteBuf byteBuf;

    public int getStreamId() {
        return streamId;
    }

    public void setStreamId(int streamId) {
        this.streamId = streamId;
    }

    protected int streamId; //流id


    protected int extTimestamp; //额外时间戳

    public int type = 1;


    private boolean lackMessage = false;

    public int getRenderTimestamp() {
        return renderTimestamp;
    }

    public void setRenderTimestamp(int renderTimestamp) {
        this.renderTimestamp = renderTimestamp;
    }

    private int renderTimestamp = 0;


    public byte[] getMessageData() {
        return messageData;
    }

    private byte[] messageData;

    private int fmt; //basic head fmt


    public void setChunkLength(int chunkLength) {
        this.chunkLength = chunkLength;
    }

    private int chunkLength = Common.DEFAULT_CHUNK_MESSAGE_LENGTH;


    private ByteBuf decoderByteBuf;


    private int readIndex = 0;

    //对应的 chunk head length 长度
    private int[] chunk_message_length = {11, 7, 3, 0};



    /**
     * 1、
     * 获取第一个字节，判断head_len 和 stream id
     * readIndex + 1
     * @return
     */
    private byte getHeadFlag(){
        byte flag = decoderByteBufRead();
        this.fmt = (byte) ((flag & 0xff & 0xff) >> 6);
        this.head_len = chunk_message_length[this.fmt];
        if(isDebug) {
           logger.info("head len "+this.head_len);
        }
        return flag;
    }


    /**
     * 2、
     * 解析csid csid 会跟后面数据 head_len 包含信息有所关联
     * readIndex + 1 或者 readIndex + 2
     * @param flag
     */
    private boolean setCsid(byte flag) {
        this.csid = (byte) ((flag & 0xff & 0xff) & 0x3f); // 按位与 11 为 1 ，有0 为 0
        switch (this.csid){
            case 0:
                if (decoderByteBuf.readableBytes() < 1) { //头部数据太少
                   logger.info("csid头部数据不足");
                    this.clearByteBuf();
                    return false;
                }
                this.csid = decoderByteBufRead() + 64;
                break;
            case 1:
                if (decoderByteBuf.readableBytes() < 2) { //头部数据太少
                   logger.info("csid2头部数据不足");
                    this.clearByteBuf();
                    return false;
                }
                this.csid = decoderByteBufRead() * 256 + decoderByteBufRead() + 64;
                break;
        }
        return true;
    }


    /**
     * 3、
     * 一、时间戳  二、设置数据长度  三、消息类型 四、straemId 五、额外时间戳
     */
    private boolean setChunkHeaderData() {
        HeadInfo headInfo = this.csidHeadInfo.get(this.csid);
        if(!chunkByteBufLength(this.head_len, getReadIndex())) {
            this.clearByteBuf();
            return false;
        }
        if (this.head_len == 0) {
            if (headInfo != null) {
                this.messageLength = headInfo.getMessageLength();
                this.timestamp = headInfo.getTimestamp();
                this.messageType = headInfo.getMessageType();
            }
        }

        if(this.head_len >= 3) {
            this.setTimestamp();
            if (headInfo != null)  {
                this.messageLength = headInfo.getMessageLength();
                this.messageType = headInfo.getMessageType();
            }
        }

        if(this.head_len >= 7){
            this.setMessageLength();
            this.setMessageType();
        }
        if(this.head_len >= 11) {
            this.setStreamId();
        }

        headInfo = new HeadInfo(this.timestamp, this.messageLength, this.messageType);
        this.csidHeadInfo.put(this.csid, headInfo);

        if((this.head_len == 3 || this.head_len == 7)){ //当头部为3 或者 7，时间戳为跟上一个时间戳的累加
            this.renderTimestamp += timestamp;
        }else if (this.head_len == 11){ //头部为11 t，时间戳直接修改
            //  logger.info("this.head_len" + timestamp);
            this.renderTimestamp = timestamp;
        }

        if(this.timestamp >= Common.TIMESTAMP_MAX_NUM) {
            if(!chunkByteBufLength(Common.EXTEND_TIMESTAMP_LENGTH,this.getReadIndex())) {
                this.clearByteBuf();
                return false;
            }
            this.extendTimestamp();
        }

        return true;
    }




    public boolean decoder(ByteBuf byteBuf) {
        this.readIndex = 0;
        this.decoderByteBuf = byteBuf.copy();
        byte flag = getHeadFlag();
        if (!setCsid(flag)){
            return false;
        }

        if (!setChunkHeaderData()){
            return false;
        }
        if(isDebug){
           logger.info("messageLen "+this.messageLength);
           logger.info("messageType "+this.messageType);
           logger.info("chunkLength "+this.chunkLength);
        }
        if (!setMessageData()) {
            return false;
        }
        byteBuf.readBytes(new byte[this.readIndex]);
        return true;
    }


    /**
     * 获取 readIndex
     * @return
     */
    private int getReadIndex(){
        return this.decoderByteBuf.readableBytes();
    }

    /**
     * head
     * 设置时间戳长度
     * readIndex + 3
     */
    private void setTimestamp() {
        byte[] timestampByte = decoderByteBufRead(Common.TIMESTAMP_BYTE_LENGTH);
        timestamp = Common.byteToInt24(timestampByte); // 为了解码 时候 客户端使用 所以 进行类加
    }

    /**
     * 设置消息长度
     * readIndex + 3
     */
    private void setMessageLength() {
        byte[] msg_len = decoderByteBufRead(Common.TIMESTAMP_BYTE_LENGTH);
        this.messageLength = Common.byteToInt24(msg_len);
    }


    /**
     * 设置数据类型
     * readIndex + 1
     */
    private void setMessageType() {
        byte type = decoderByteBufRead();
        switch (type) {
            case 0x01:
                this.messageType = RtmpMessage.ChunkSize;
                break;
            case 0x03:
                this.messageType = RtmpMessage.BYTES_READ;
                break;
            case 0x04:
                this.messageType = RtmpMessage.PING;
                break;
            case 0x05:
                this.messageType = RtmpMessage.SERVER_WINDOW;
                break;
            case 0x08:
                this.messageType = RtmpMessage.AUDIO;
                //  this.audioTimestamp = timestamp;
                break;
            case 0x09:
                this.messageType = RtmpMessage.VIDEO;
                // this.videoTimestamp = timestamp;
                break;
            case 0x12:
                this.messageType = RtmpMessage.NOTIFY;
                break;
            case 0x14:
                this.messageType = RtmpMessage.Control;
                break;
            default:
                logger.info(Common.bytes2hex(new byte[]{type}));
                this.messageType = RtmpMessage.NOT;
               logger.info("数据大小"+chunkLength);
               logger.info("未知消息" + type);
        }
    }

    /**
     * 设置流id
     * readIndex + 4
     */
    private void setStreamId(){
        byte[] streamByte = decoderByteBufRead(Common.STREAM_ID_LENGTH);
        this.streamId = Common.byteSmallToInt(streamByte); //只有 stream 是小端模式
    }


    /**
     * 设置额外时间戳
     * readIndex + 4
     */
    private void extendTimestamp(){
       logger.info("额外数据" + this.timestamp);
        this.head_len = this.head_len + Common.EXTEND_TIMESTAMP_LENGTH;
        byte[] timestampByte = decoderByteBufRead(Common.EXTEND_TIMESTAMP_LENGTH);
        this.extTimestamp = Common.byteToInt(timestampByte);
        if(this.messageType == RtmpMessage.AUDIO || this.messageType == RtmpMessage.VIDEO ){
            this.renderTimestamp = this.extTimestamp;
        }
    }

    /**
     * 解码数据清理
     */
    private void clearByteBuf() {
        int len = this.decoderByteBuf.readableBytes();
        byte[] data = new byte[len];
        if (this.decoderByteBuf.refCnt() > 0) {
            this.decoderByteBuf.readBytes(data);
            this.decoderByteBuf.release();
        }

    }

    /**
     * 检查是否数据不足
     * @param len
     * @return
     */
    private boolean chunkByteBufLength(int len,int dataLen) {
        if(dataLen < len) {
            return false;
        }
        return true;
    }


    /**
     * 读取一个字节
     * @return
     */
    private byte decoderByteBufRead(){
        this.readIndex += 1;
        return this.decoderByteBuf.readByte();
    }

    /**
     * 数据读取统一方法，方便计算读取长度
     * @param messageLength
     * @return
     */
    private byte[] decoderByteBufRead(int messageLength){
        byte[] msg = new byte[messageLength];
        this.readIndex += messageLength;
        this.decoderByteBuf.readBytes(msg);
        return msg;
    }

    /**
     * 当数据包超过chunkSize 设置时候，需要分包，每个包 head_len为 0
     */
    private boolean subPackage() {
        int messageReadIndex = 0;
        ByteBuf messageByteBuf = ByteBufAllocator.DEFAULT.buffer(1024);
        this.messageData = new byte[this.messageLength];
        while (messageReadIndex < this.messageLength) {
            if (messageReadIndex + chunkLength <= this.messageLength) { //还需要分包
                if(!chunkByteBufLength(chunkLength, getReadIndex())) {
                    this.clearByteBuf();
                    messageByteBuf.release();
                    return false;
                }
                messageByteBuf.writeBytes(decoderByteBufRead(chunkLength));
                messageReadIndex += chunkLength;
                if (messageReadIndex < this.messageLength) { //数据还不够的情况，读掉head 头部一个字节
                    if(!chunkByteBufLength(1, getReadIndex())) {
                        this.clearByteBuf();
                        messageByteBuf.release();
                        return false;
                    }
                    decoderByteBufRead(1);
                }
            } else {
                int lastMessageLength = this.messageLength - messageReadIndex;
                if(!chunkByteBufLength(lastMessageLength, getReadIndex())) {
                    this.clearByteBuf();
                    messageByteBuf.release();
                    return false;
                }
                messageByteBuf.writeBytes(decoderByteBufRead(lastMessageLength));
                break;
            }
        }
        messageByteBuf.readBytes(this.messageData);
        messageByteBuf.release();
        return true;
    }

    /**
     * 设置消息数据
     */
    private boolean setMessageData() {
        if(this.messageLength > chunkLength) { //大数据,rtmp 分包提取
            return subPackage();
        }
        if(!chunkByteBufLength(this.messageLength, getReadIndex())) {
            if(this.type == 1){
               logger.info("数据不足: messageLength : " + this.messageLength + " , bytebuffer length : " + getReadIndex());
            }
            this.clearByteBuf();
            return false;
        }

        messageData = decoderByteBufRead(messageLength);

        if (isDebug){
           logger.info("setMessageData" + this.decoderByteBuf.refCnt());
           logger.info("setMessageData len" + this.getReadIndex());
           logger.info(Common.bytes2hex(messageData));
        }
        return true;
    }

    public void release(){
        this.clearByteBuf();
    }
}


